package dp.mobile.game;

import java.util.ArrayList;

import org.w3c.dom.Node;

import android.util.Log;
import dp.mobile.comm.worker.BluetoothHandler;
import dp.mobile.eden.MultiplayerAct;
import dp.mobile.eden.multiplayer.message.action.ActionUpdate;
import dp.mobile.eden.multiplayer.message.action.BuildGenerator;
import dp.mobile.eden.multiplayer.message.action.BuildTower;
import dp.mobile.eden.multiplayer.message.action.RemoveEnemy;
import dp.mobile.eden.multiplayer.message.action.SpawnEnemy;
import dp.mobile.eden.multiplayer.message.action.UpgradeGenerator;
import dp.mobile.eden.multiplayer.message.action.UpgradeTower;
import dp.mobile.entity.Enemy;
import dp.mobile.entity.generator.Generator;
import dp.mobile.entity.generator.GeneratorFactory;
import dp.mobile.entity.tower.Tower;
import dp.mobile.entity.tower.TowerFactory;
import dp.mobile.glwrapper.GLTexture;
import dp.mobile.glwrapper.GLTiledTexture;
import dp.mobile.glwrapper.GameRenderer;
import dp.mobile.level.Level;
import dp.mobile.level.Wave;
import dp.mobile.system.Utilities;
import dp.mobile.system.XMLReader;

public class MultiplayerGame extends Game {
	private boolean isServer = false;
	private long mPropDelay;
	private Player mOpponent;
	private ArrayList<Enemy> mDeletedEnemies = new ArrayList<Enemy>();
	
	public MultiplayerGame(String levelSource , boolean server , long delay) {
		super(levelSource);
		isServer = server;
		mPropDelay = delay;
		
		int p1index, p2index;
		if (isServer) {
			p1index = 0;
			p2index = 1;
		}
		else {
			p1index = 1;
			p2index = 0;
		}
		
		///Init players
		mOpponent = new Player(new Level(XMLReader.instance().parseXML(Utilities.LEVEL_FOLDER + "/" + levelSource)) , p2index);
		mPlayer.setImageIndex(p1index);
		
		///Init nodes owner
		initNodesOwner();
		
		loadEnemyImage("enemy1");
		loadEnemyImage("enemy2");
		loadEnemyImage("enemy3");
		loadEnemyImage("enemy4");
	}
	
	@Override
	protected void initNodesOwner() {
		int i = 0;
		Player side1, side2;
		GLTexture offImage = null;
		GLTexture onImage = null;
		
		if (isServer) {
			side1 = getPlayer();
			side2 = getOpponent();
		}
		else {
			side2 = getPlayer();
			side1 = getOpponent();
		}
		
		offImage = GameRenderer.instance().allocateTexture(Utilities.TILESET_FOLDER + "/node642.png");
		onImage = GameRenderer.instance().allocateTexture(Utilities.TILESET_FOLDER + "/node64act2.png");
		
		for (dp.mobile.entity.Node node : mNodes) {
			i++;
			if (i <= mNodes.size() / 2) {
				node.setPlayer(side1);
			}
			else {
				node.setPlayer(side2);
				node.setOfflineTexture(offImage);
				node.setOnlineTexture(onImage);
			}
		}
	}
	
	public Player getOpponent() {
		return mOpponent;
	}
	
	public boolean isServer() {
		return isServer;
	}
	
	public long getPropDelay() {
		return mPropDelay;
	}
	
	public void handleActionUpdateMessage(ActionUpdate msg) {
		if (msg.getAction() == ActionUpdate.ACTION_BUILD_TOWER) {
			Log.i("this" , msg.toString());
			BuildTower b = (BuildTower) msg;
			Tower t = new Tower(TowerFactory.instance().getTowerData(1), getOpponent(), b.pos_x , b.pos_y, getElapsedGameTime(), true);
			t.setID(b.getEntityID());
			addLocalTower(t);
		}
		else if (msg.getAction() == ActionUpdate.ACTION_BUILD_GENERATOR) {
			BuildGenerator g = (BuildGenerator) msg;
			Generator gen = new Generator(GeneratorFactory.instance().getBaseGenerator(), getOpponent(), g.pos_x, g.pos_y, getElapsedGameTime(), true);
			gen.setID(g.getEntityID());
			addLocalGenerator(gen);
			Log.i("this2" , "BUILD GENERATOR");
		}
		else if (msg.getAction() == ActionUpdate.ACTION_SPAWN_ENEMY) {
			//if (!isServer) {
			SpawnEnemy m = (SpawnEnemy)msg;
			for (int i = 0 ; i < m.mOwners.size() ; i++) {
				Enemy e;
				/*if (m.mOwners.get(i)) { //punya server
					Log.i("this" , "BELONGS TO SERVER");
					e = new Enemy(mLevel.getWave(m.mWaves.get(i)).getEnemyData(), mLevel.getWave(mPlayer.getWave()).getEnemyImages(), mLevel, 0, getOpponent());	
				}
				else { //punya client
					Log.i("this" , "BELONGS TO CLIENT");
					
				}*/
				int side = 1;
				if (!isServer)
					side = 0;
				
				e = new Enemy(mLevel.getWave(m.mWaves.get(i)).getEnemyData(), mLevel.getWave(mPlayer.getWave()).getEnemyImages(), mLevel, side, getPlayer());
				e.setID(m.mEntityIds.get(i));
				e.setHealth(m.mInitialHealths.get(i));
				e.setMaxHealth(m.mMaxHealths.get(i));
				e.setVelocity(m.mVelocities.get(i));
				e.setGold(m.mGolds.get(i));
				e.setImages(mEnemyImagesSequence.get(m.mImageIndex.get(i)));
				
				//mDeletedEnemies.put(e.getID(), false);
				mEnemies.add(e);
			}
			//}
		}
		else if (msg.getAction() == ActionUpdate.ACTION_REMOVE_ENEMY) {
			RemoveEnemy re = (RemoveEnemy) msg;
			//ArrayList<Enemy> DeletedEnemies = new ArrayList<Enemy>();
			for (Integer id : re.mDeletedIds) {
				for (Enemy e : mEnemies) {
					if (e.getID() == id)
						mDeletedEnemies.add(e);
				}
			}
			//super.deleteEnemies(DeletedEnemies);
		}
		else if (msg.getAction() == ActionUpdate.ACTION_UPGRADE_TOWER) {
			UpgradeTower ut = (UpgradeTower) msg;
			Tower target = null;
			for (Tower t : mTowers) {
				if (!t.getPlayer().equals(getPlayer()) && t.getID() == ut.getEntityID()) {
					target = t;
				}
			}
			if (target != null) {
				upgradeLocalTower(target, ut.getUpgradeIndex(), true);
			}
		}
		else if (msg.getAction() == ActionUpdate.ACTION_UPGRADE_GENERATOR) {
			UpgradeGenerator ug = (UpgradeGenerator) msg;
			Generator target = null;
			for (Generator g : mGenerators) {
				if (!g.getPlayer().equals(getPlayer()) && g.getID() == msg.getEntityID()) {
					target = g;
				}
			}
			if (target != null) {
				upgradeLocalGenerator(target, ug.getComponentIndex());
			}
		}
		else if (msg.getAction() == ActionUpdate.ACTION_DISMANTLE_TOWER) {
			Tower target = null;
			for (Tower t : mTowers) {
				if (!t.getPlayer().equals(getPlayer()) && t.getID() == msg.getEntityID()) {
					target = t;
				}
			}
			if (target != null) {
				dismantleLocalTower(target);
			}
		}
		else if (msg.getAction() == ActionUpdate.ACTION_DISMANTLE_GENERATOR) {
			Generator target = null;
			for (Generator g : mGenerators) {
				if (!g.getPlayer().equals(getPlayer()) && g.getID() == msg.getEntityID()) {
					target = g;
				}
			}
			if (target != null) {
				dismantleLocalGenerator(target);
			}
		}
		//BluetoothHandler.getInstance().sendObjectId(MULTIPLAYER_ACK_ID);
	}
	
	
	/**
	 * Sets the approximate delay for a game object
	 * @param delay propagation delay to be set
	 */
	public void setPropDelay(long delay) {
		mPropDelay = delay;
	}
	
	@Override
	public void endGame() {
		if (mListener != null) {
			mListener.onGameOver();
		}
	}
	
	@Override
	protected void deleteEnemies(ArrayList<Enemy> DeletedEnemies) {
		RemoveEnemy re = new RemoveEnemy(getElapsedGameTime(), isServer);
		for(Enemy e : DeletedEnemies) {
			if (e.getPlayer().equals(getPlayer()))
				re.mDeletedIds.add(e.getID());
		}
		
		DeletedEnemies.addAll(mDeletedEnemies);
		mDeletedEnemies.clear();
		
		super.deleteEnemies(DeletedEnemies);
		if (re.mDeletedIds.size() > 0) {
			BluetoothHandler.getInstance().sendObjectId(MultiplayerAct.MULTIPLAYER_OBJ_ID);
			BluetoothHandler.getInstance().send(re);
		}
	}
	
	private int mWave = 0;
	private ArrayList<GLTiledTexture[][]> mEnemyImagesSequence = new ArrayList<GLTiledTexture[][]>();
	
	public void loadEnemyImage(String mEnemyFile) {
		//Set data
		int Row		= 1;
		int Column	= 4;

		//Create image array
		GLTiledTexture[][] mEnemyImages	= new GLTiledTexture[4][Row * Column];
		
		//For each direction
		for (int j = 0; j < 4; j++) {
			//Get image data
			GLTexture mainText	= GameRenderer.instance().allocateTexture(Utilities.ENEMY_FOLDER + "/" + mEnemyFile + "_" + j + ".png");
			//For each frame
			for (int l = 0; l < mEnemyImages[j].length; l++) {
				final float left 	= (float)l 		/ (float)mEnemyImages[j].length;
				final float right 	= (float)(l + 1)/ (float)mEnemyImages[j].length;
				
				float[] crop = new float[] {
					left,	1,
					right,	1,
					left,	0,
					right,	0
				};
				
				mEnemyImages[j][l] = new GLTiledTexture(mainText, crop);
			}
		}
		
		mEnemyImagesSequence.add(mEnemyImages);
	}
	
	private int manipulateEnemy(Enemy e) {
		double rand = Math.random();
		int retval = -1;
		
		if (rand <= 0.25) {
			e.setHealth(80 + mWave * 2);
			e.setMaxHealth(80 + mWave * 2);
			e.setVelocity(32);
			e.setGold(2 + (int)Math.round(mWave * 0.1));
			e.setImages(mEnemyImagesSequence.get(0));
			retval = 0;
		}
		else if (rand > 0.25 && rand <= 0.50) {
			e.setHealth(100 + mWave * 4);
			e.setMaxHealth(100 + mWave * 4);
			e.setVelocity(16);
			e.setGold(3 + (int)Math.round(mWave * 0.4));
			e.setImages(mEnemyImagesSequence.get(1));
			retval = 1;
		}
		else if (rand > 0.50 && rand <= 0.75) {
			e.setHealth(110 + mWave * 3);
			e.setMaxHealth(110 + mWave * 3);
			e.setVelocity(20);
			e.setGold(4 + (int)Math.round(mWave * 0.5));
			e.setImages(mEnemyImagesSequence.get(2));
			retval = 2;
		}
		else if (rand > 0.75 && rand <= 1) {
			e.setHealth(120 + mWave * 2);
			e.setMaxHealth(120 + mWave * 2);
			e.setVelocity(14);
			e.setGold(3 + (int)Math.round(mWave * 0.8));
			e.setImages(mEnemyImagesSequence.get(3));
			retval = 3;
		}
		return retval;
	}
	
	@Override
	protected void spawnEnemies(Node[] EnemyData) {
		if (EnemyData != null) {
			//if (isServer) {
				GameRenderer.instance().fakeInvalidate();
				
				//While there's still an enemy
				int i = 0;
				
				SpawnEnemy se = new SpawnEnemy(getElapsedGameTime(), isServer);
				int side = 0;
				
				if (!isServer)
					side = 1;
				
				while (i < EnemyData.length) {
					//Add to the list
					Enemy e = new Enemy(
							EnemyData[i],
							mLevel.getWave(mPlayer.getWave()).getEnemyImages(),
							mLevel,
							side,
							getOpponent());
					
					//TODO: calculate enemy health
					//e.setHealth(100);
					int index = manipulateEnemy(e);
					mEnemies.add(e);
					se.addEnemyMeta(e.getID(), e.getHealth() , e.getMaxHealth() , e.getVelocity() , e.getGold() , index, true, mPlayer.getWave());
					
					i++;
				}
				
				BluetoothHandler.getInstance().sendObjectId(MultiplayerAct.MULTIPLAYER_OBJ_ID);
				BluetoothHandler.getInstance().send(se);
				Log.i("this" , se.mEntityIds.size() + "");
			}
	        
			int count = 0;
			for (Enemy e : mEnemies)
				if (!e.getPlayer().equals(getPlayer()))
					count++;
		
	        if (mLevel.getWave(mPlayer.getWave()).isWaveFinished() && count == 0){
	        	mWave++;
				Wave wave = mLevel.getWave(mPlayer.getWave());
	        	
	        	wave.setEnemyCount(0);
	        	wave.setMaxEnemy(12);
	        	wave.setSpawnDelay(3000);
	        	wave.setSpawnTimer(0);
	        }
		//}
	}
	
	/**
	 * 
	 * ADDING AND UPGRADING SECTION
	 * 
	 */
	
	@Override
	public void afterAddTower(Tower t) {
		if (t.getPlayer().equals(getPlayer()))
			broadcastAddTower(getElapsedGameTime() , t);
	}
	
	public void addLocalTower(Tower t) {
		super.addTower(t, true);
	}
	
	@Override
	public void afterAddGenerator(Generator g) {
		if (g.getPlayer().equals(getPlayer()))
			broadcastAddGenerator(getElapsedGameTime(), g);
	}
	
	public void addLocalGenerator(Generator g) {
		super.addGenerator(g, true);
	}
	
	@Override
	protected void afterUpgradeTower(Tower t, int towerDataIndex) {
		if (t.getPlayer().equals(getPlayer()))
			broadcastUpgradeTower(getElapsedGameTime(), t, towerDataIndex);
	}
	
	public void upgradeLocalTower(Tower t, int towerDataIndex, boolean isDelayed) {
		upgradeTower(t, towerDataIndex, true);
	}
	
	@Override
	protected void afterUpgradeGenerator(Generator g, int component) {
		if (g.getPlayer().equals(getPlayer()))
			broadcastUpgradeGenerator(getElapsedGameTime(), g, component);
	}
	
	public void upgradeLocalGenerator(Generator g, int component) {
		upgradeGenerator(g, component, true);
	}
	
	@Override
	protected void afterDismantleTower(Tower t) {
		if (t.getPlayer().equals(getPlayer()))
			broadcastActionUpdate(getElapsedGameTime(), new ActionUpdate(getElapsedGameTime(), ActionUpdate.ACTION_DISMANTLE_TOWER, isServer, t.getID()));
	}
	
	public void dismantleLocalTower(Tower t) {
		dismantleTower(t, true);
	}
	
	@Override
	protected void afterDismantleGenerator(Generator g) {
		if (g.getPlayer().equals(getPlayer()))
			broadcastActionUpdate(getElapsedGameTime(), new ActionUpdate(getElapsedGameTime(), ActionUpdate.ACTION_DISMANTLE_GENERATOR, isServer, g.getID()));
	}
	
	public void dismantleLocalGenerator(Generator g) {
		super.dismantleGenerator(g, true);
	}
	
	private void broadcastActionUpdate(long gameTime , ActionUpdate msg) {
		BluetoothHandler.getInstance().sendObjectId(MultiplayerAct.MULTIPLAYER_OBJ_ID);
		BluetoothHandler.getInstance().send(msg);
	}

	public void broadcastAddTower(long gameTime, Tower t) {
		BuildTower m = new BuildTower(gameTime , isServer() , t.getID());
		m.pos_x = t.getTileX();
		m.pos_y = t.getTileY();
		broadcastActionUpdate(gameTime, m);
	}
	
	public void broadcastAddGenerator(long gameTime , Generator g) {
		BuildGenerator m = new BuildGenerator(gameTime , isServer() , g.getID());
		m.pos_x = g.getTileX();
		m.pos_y = g.getTileY();
		broadcastActionUpdate(gameTime, m);
	}
	
	public void broadcastUpgradeTower(long gameTime , Tower t , int towerDataIndex) {
		UpgradeTower m = new UpgradeTower(gameTime, isServer, t.getID(), towerDataIndex);
		broadcastActionUpdate(gameTime, m);
	}
	
	public void broadcastUpgradeGenerator(long gameTime , Generator g , int componentIndex) {
		UpgradeGenerator m = new UpgradeGenerator(gameTime, isServer, g.getID(), componentIndex);
		broadcastActionUpdate(gameTime, m);
	}
}
